22.3 Elementare Klassen für grafische Operationen  
Als ich Ihnen die Zeichenmethoden von Graphics vorgestellt habe, mussten wir den DrawXxx-Methoden ein Pen-Objekt und den FillXxx-Methoden ein Brush-Objekt übergeben. Es wird Zeit, dass wir uns nun etwas näher mit diesen beiden Typen auseinander setzen.
Der englische Begriff »Pen« bedeutet ins Deutsche übersetzt Stift, »Brush« kann mit Pinsel übersetzt werden. Sie können diese Begriffe auch auf die Funktionalität der Typen abbilden: Mit einem Stift werden Striche gezeichnet, mit einem Pinsel großflächig Farben aufgetragen.
22.3.1 Die Klasse »Brush«  
Ein Objekt vom Typ Brush dient dazu, ein grafisches Objekt mit einer Farbe oder sogar einem Muster zu füllen. Allerdings ist Brush abstrakt definiert, was zur Folge hat, dass die Klasse nicht instanziiert werden kann. Dafür werden aber fünf abgeleitete Klassen bereitgestellt, die jeweils einem bestimmten Einsatzzweck dienen:
|
System.Drawing.SolidBrush |
|
System.Drawing.Drawing2D.HatchBrush |
|
System.Drawing.TextureBrush |
|
System.Drawing.Drawing2D.LinearGradienBrush |
|
System.Drawing.Drawing2D.PathGradientBrush |
Brush gehört dem Namespace System.Drawing an, drei der abgeleiteten Klassen sind jedoch Mitglieder von System.Drawing.Drawing2D.
Die Klasse »SolidBrush«
Fangen wir mit der einfachsten Klasse an, die wir in den vorhergehenden Beispielen auch schon benutzt haben. SolidBrush hat nur einen Konstruktor, dem die Füllfarbe des grafischen Objekts vom Typ Color übergeben wird. Mit
| e.Graphics.FillEllipse(New SolidBrush(Color.Blue), _
|
| 0, 0, 100, 200)
|
zeichnen Sie zum Beispiel eine blau gefüllte Ellipse. Sie haben allerdings auch noch eine andere Möglichkeit, bei der Sie kein Brush-Objekt erzeugen müssen. Dazu übergeben Sie eine vordefinierte Konstante der Klasse Brushes:
| e.Graphics.FillEllipse(Brushes.Blue, 0, 0, 100, 200)
|
Die Konstantenbezeichner entsprechen den der in der Klasse Color vordefinierten Farben.
Füllmuster mit »HatchBrush«
Mit SolidBrush wird ein Grafikobjekt gleichmäßig mit einer Farbe gefüllt. Das sieht auf einem Bildschirm sehr gut aus, kann aber bei der Ausgabe auf einem Schwarz-Weiß-Drucker zu Problemen bei der Objekterkennung führen. In solchen Fällen ist es oft besser, die Grafikobjekte mit einem Muster, beispielsweise einer Schraffur, zu füllen. HashBrush kann das.
Eine mit einer Schraffur gefüllte Fläche ist durch drei Charakteristika gekennzeichnet: die Vordergrundfarbe, die Hintergrundfarbe und das Schraffurmuster. Diese Eigenschaften spiegeln sich auch in den beiden Konstruktoren der Klasse HatchBrush wider.
| Public Sub New (HatchStyle, Color foreColor)
|
| Public Sub New (HatchStyle, Color, Color)
|
Dem zuerst genannten Konstruktor wird die Vordergrundfarbe übergeben, dem zweiten zuerst die Vorder- und anschließend die Hintergrundfarbe.
Aus den Parametern des dreiparametrigen Konstruktors kann geschlossen werden, dass sich ein HatchBrush-Objekt durch drei Eigenschaften auszeichnet:
|
HatchStyle |
|
ForegroundColor |
|
BackgroundColor |
Die beiden Farbeigenschaften sind vom Typ Color, HatchStyle ist vom Typ der gleichnamigen Enumeration, die vordefinierte Schraffurstile enthält.
Die Mitglieder der HatchStyle-Enumeration hier einzeln aufzuführen, würde zu weit führen. Stattdessen wollen wir uns alle Schraffurstile in einem Fenster in Rechtecken anzeigen lassen.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 22\HatchBrush-Muster
|
| ' ---------------------------------------------------------
|
| Imports System.Drawing.Drawing2D
|
| Public Class Form1
|
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| Dim g As Graphics = e.Graphics
|
| Dim b As Integer = 0
|
| Dim h As Integer = 0
|
| Dim i As Integer = 0
|
| Dim hBrush As HatchBrush
|
| ' die Enumeration "HatchStyle" durchlaufen und alle
|
| ' Mitglieder auswerten
|
| For Each hs As HatchStyle In _
|
| [Enum].GetValues(GetType(HatchStyle))
|
| b = 10 + (i Mod 6) * 80
|
| h = Convert.ToInt32(i \ 6) * 50
|
| hBrush = New HatchBrush(hs, Color.Black, Color.White)
|
| g.FillRectangle(hBrush, b, h + 15, 70, 40)
|
| i += 1
|
| hBrush.Dispose()
|
| Next
|
| End Sub
|
| End Class
|
Um auf alle Mitglieder der Enumeration HatchStyle zugreifen zu können, rufen wir die statische Methode GetValues der Klasse Enum auf. Übergeben müssen wir dem Methodenaufruf den Typ Type von HatchStyle, den uns die Funktion GetType liefert.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.12 Die »HatchBrush«-Muster
Wenn der Clientbereich einer Form oder Picturebox mit einem Schraffurmuster gefüllt wird, wird das Muster einfach periodisch in horizontaler und vertikaler Richtung wiederholt. Der Ursprung der Muster entspricht dabei dem Ursprung des Graphics-Objekts und ist damit auch der Ursprung des Clientbereichs.
Die Eigenschaft RenderingOrigin des Graphics-Objekts bietet die Möglichkeit, den Ursprungspunkt der Schraffuren nach eigenem Ermessen festzulegen, z. B.:
| e.Graphics.RenderingOrigin(25, 69)
|
Es stellt sich jetzt die Frage, welchen Vorteil wir aus der Eigenschaft RenderingOrigin ziehen können. Betrachten Sie dazu den folgenden Code, der zu der in Abbildung 22.13 gezeigten Form führt.
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| Dim g As Graphics = e.Graphics
|
| Dim tb As HatchBrush = New HatchBrush(HatchStyle.Plaid, _
|
| Color.White)
|
| For i As Integer = 0 To 3
|
| g.FillRectangle(tb, 30 * i, 20 * i, 80, 50)
|
| Next
|
| End Sub
|
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.13 Füllmuster mit fließendem Übergang
Wir sehen, dass die Füllmuster fließend ineinander übergehen. Die Überlappung der vier Rechtecke ist nicht erkennbar. Ergänzen wir nun die For-Schleife mit der RenderingOrigin-Eigenschaft wie folgt:
| ...
|
| For i As Integer = 0 To 3
|
| g.RenderingOrigin = New Point(30 * i, 20 * i)
|
| g.FillRectangle(tb, 30 * i, 20 * i, 80, 50)
|
| Next
|
Nun sind die Auswirkungen ganz offensichtlich: Die Rechtecke überlappen sich, weil sich das Füllmuster nicht mehr kontinuierlich über die Graphics-Fläche spannt.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.14 Füllmuster, festgelegt mit »RenderingOrigin«
Benutzerdefinierte Schraffuren mit »TextureBrush«
Vielleicht reichen Ihnen die vordefinierten Füllmuster der Enumeration HatchStyle nicht aus, oder Sie können Ihrem kreativen Schaffensdrang nicht Einhalt gebieten und wollen eigene Muster zur Füllung aller mit den FillXxx-Methoden gezeichneten Grafikobjekte verwenden. Dann müssen Sie den grafischen Methoden ein Objekt vom Typ TextureBrush übergeben.
TextureBrush definiert eine Reihe von Konstruktoren. Dem einfachsten übergeben Sie bei der Instanziierung die Referenz auf ein Image-Objekt:
Image ist eine abstrakte Klasse, aus der die beiden Klassen Bitmap und Metafile abgeleitet werden.
Angenommen Sie hätten ein eigenes Bitmap gezeichnet und es in dem Ordner gespeichert, der die ausführbare Datei der Anwendung enthält. Dann könnten Sie sich unter Aufruf der statischen Methode FromFile die Referenz auf die Datei besorgen und diese dem Konstruktor übergeben:
| Dim g As Graphics = e.Graphics
|
| Dim tb As TextureBrush = _
|
| New TextureBrush(Image.FromFile("..\..\Bitmap1.bmp"))
|
| g.FillRectangle(tb, 0, 0, Me.ClientSize.Width, _
|
| Me.ClientSize.Height)
|
Mit diesem Programmcode wird die durch das Graphics-Objekt beschriebene Zeichenfläche ausgehend vom Koordinatenursprung (0, 0) mit dem Bitmap gefüllt. Das könnte beispielsweise wie in der folgenden Abbildung gezeigt aussehen. Das Bitmap hat dabei eine Größe von 50 x 50 Pixel.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.15 Füllmuster, festgelegt mit »TextureBrush«
Nehmen wir jetzt an, das zu füllende Rechteck hätte seinen eigenen Ursprung bezüglich seiner Clientfläche nicht im Punkt (0, 0), sondern in (25, 25). Die Breite des Rechtecks sei 215 Pixel, seine Höhe 100 Pixel:
| ...
|
| g.FillRectangle(tb, 25, 25, 215, 100)
|
Die Positionierung der Bitmaps auf der Zeichenfläche ist standardmäßig an das Graphics-Objekt und dessen Ursprung gebunden, der immer in (0, 0) des zugrunde liegenden Clientbereichs liegt.
Ein zu zeichnendes Rechteck kann man sich wie ein Fenster auf der Zeichenfläche vorstellen, das nur einen Teilausschnitt sichtbar macht. Weil sich aber die Füllmuster nicht an den Ursprungskoordinaten des Rechtecks, sondern weiterhin am Ursprung der Zeichenfläche orientieren, werden wir die Form wie in der folgenden Abbildung gezeigt sehen.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.16 Mit »TextureBrush« verschobenes Füllmuster
Lineare Verläufe mit »LinearGradientBrush«
Kommen wir jetzt zur nächsten Brush-Klasse, die uns zu weiteren Farbspielereien geradezu ermutigt: LinearGradientBrush. Vielleicht sehen wir uns gleich zu Anfang ein Beispiel an, das auf dieser Klasse basiert.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.17 Farbverlauf mit »LinearGradientBrush«
Wir sehen einen Farbverlauf, der sich von Weiß kontinuierlich nach Schwarz fortsetzt. Dies ist das Resultat der Klasse LinearGradientBrush. Der Programmcode, der dieser Abbildung zugrunde liegt, ist wie folgt:
| Dim linearBrush As LinearGradientBrush = _
|
| New LinearGradientBrush(New Point(0, 150), _
|
| New Point(300, 150), Color.White, Color.Black)
|
| e.Graphics.FillRectangle(linearBrush, 0, 0, 300, 150)
|
Wie Sie unschwer erkennen können, sind nur zwei Zeilen Code notwendig.
Wenden wir uns nun, nachdem Sie wissen, wozu die Klasse dient, den Details zu. LinearGradientBrush definiert insgesamt acht Konstruktoren. Im Codefragment oben wurde der folgende benutzt:
| Public Sub New (point1 As Point, point2 As Point, _
|
| color1 As Color, color2 As Color)
|
Die Definition des Farbverlaufs setzt zwei Punkte voraus: den Startpunkt des Farbverlaufs (point1) und den Endpunkt (point2). Beiden Punkten ist eine Farbe zugeordnet: dem Punkt point1 die Farbe color1, dem Punkt point2 die Farbe color2. Zwischen diesen beiden Punkten verläuft die Farbe von color1 kontinuierlich nach color2.
Die Frage, ob der Farbverlauf horizontal oder vertikal orientiert ist, hängt von der Festlegung der beiden Punkte ab. Stellen Sie sich dazu eine Verbindungslinie zwischen point1 und point2 vor. Senkrecht zu dieser werden die farbgleichen Pixelbalken gezeichnet. Daraus folgt, dass für den Fall, dass die Verbindungslinie horizontal ist, der Farbverlauf vertikal gezeichnet wird, bei einer vertikalen Verbindungslinie der Farbverlauf horizontal dargestellt wird. Selbstverständlich können Farbverläufe auch winklig im Raum stehen.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.18 Definition der Farbverläufe
Die Klasse »PathGradientBrush«
Wollen Sie wissen, wie man die in der folgenden Abbildung gezeigte, ziemlich beeindruckende Figur programmiert?
Eigentlich ist es kaum zu fassen, aber tatsächlich genügen dazu nur drei Zeilen Programmcode:
| Dim pt() As Point = New Point() {New Point(30, 30), _
|
| New Point(0, 200), _
|
| New Point(300, 200), _
|
| New Point(140, 160), _
|
| New Point(200, 0)}
|
| Dim p As PathGradientBrush = New PathGradientBrush(pt)
|
| e.Graphics.FillRectangle(p, 0, 0, 300, 300)
|
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.19 Beispiel für die Klasse »PathGradientBrush«
Wenn Sie nicht vor ausgesprochen langen Codezeilen zurückschrecken, können Sie den Code sogar auf eine Zeile reduzieren!
Basis ist die fünfte und letzte Klasse, die aus der Basisklasse Brush abgeleitet wird: PathGradientBrush. Ich habe im Codefragment den Konstruktor gewählt, der ein Point-Array entgegennimmt. Aus dem Point-Array wird ein Polygon gebildet und mit den Standardfarben gefüllt.
PathGradientBrush ist die allgemeinste aller Brush-Klassen, die Verläufe zwischen Punktpaaren erzeugt. Wollen Sie die Farben selbst bestimmen, übergeben Sie einfach den gewünschten Farbwert an die Eigenschaften CenterColor und/oder SurroundColor. Letztere nimmt sogar ein Color-Array entgegen, um mehrere Randfarben der Figur festzulegen. Mit der Eigenschaft CenterPoint lässt sich der Mittelpunkt des Polygons individuell festlegen. Er darf sogar außerhalb der zu zeichnenden Figur liegen.
Im nächsten Codefragment werden die Eigenschaften dazu benutzt, um einen Kreis als schräg angestrahlte Kugel erscheinen zu lassen.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.20 Dreidimensional erscheinender Kreis gezeichnet mit »PathGradientBrush«
| Dim graphPath As GraphicsPath = New GraphicsPath()
|
| graphPath.AddEllipse(20, 20, 170, 170)
|
| Dim brush As PathGradientBrush = _
|
| New PathGradientBrush(graphPath)
|
| brush.CenterPoint = New Point(130, 130)
|
| brush.CenterColor = Color.White
|
| brush.SurroundColors = New Color() {Color.Green}
|
| e.Graphics.FillRectangle(brush, 0, 0, 300, 300)
|
In diesem Beispiel kommt ein anderer Konstruktor zum Einsatz, der ein Übergabeargument vom Typ GraphicsPath erwartet:
| Public Sub New(GraphicsPath)
|
GraphicsPath ist eine nicht ableitbare Klasse und repräsentiert eine Reihe verbundener Linien und Kurven, die über Methoden wie beispielsweise AddRectangle, AddLine usw. zu einer geometrischen Figur geformt werden. Im Code wurde die Methode AddEllipse dazu benutzt, einen Kreis festzulegen. Der Punkt, der die Position der inneren Farbe beschreibt, ist außerhalb der Mitte des Kreises festgelegt. Die innere Farbe ist Weiß, die Randfarbe Grün.
22.3.2 Die Klasse »Pen«  
Die Klasse Pen ist einem Stift vergleichbar, der Linien in einem festgelegten Format zeichnet. Vier Konstruktoren stehen uns zur Verfügung, um diesen Zeichenstift zu realisieren.
| Public Sub New (Brush)
|
| Public Sub New (Color)
|
| Public Sub New (Brush, Single)
|
| Public Sub New (Color, Single)
|
Sie müssen also in jedem Fall eine Brush-Referenz oder mit Color einen Farbwert übergeben und können über die Übergabe eines zweiten Parameters auch die Strichbreite bestimmen.
Das Zeichnen unterschiedlicher Linienstile
Erzeugen Sie ein Pen-Objekt, wird standardmäßig eine durchgehende Linie gezeichnet. Das muss aber nicht so sein, denn mit der Eigenschaft DashStyle können wir das Linienmuster auch anderweitig festlegen:
| Public Property DashStyle As DashStyle
|
Um zu wissen, welche Linienstile zur Auswahl stehen, müssen wir einen Blick in die DashStyle-Enumeration werfen.
Tabelle 22.3 Mitglieder der Enumeration »DashStyle«
| DashStyle-Member
|
Beschreibung
|
| Solid
|
Durchgezogene Linie
|
| Dash
|
Gestrichelte Linie
|
| Dot
|
Gepunktete Linie
|
| DashDot
|
Abwechselnd Strich-Punkt
|
| DashDotDot
|
Abwechselnd Strich-Punkt-Punkt
|
| Custom
|
Benutzerdefiniert
|
Die ersten fünf Konstanten sind trivial, die letzte, DashStyle.Custom, sollten wir uns noch etwas genauer ansehen.
Standardmäßig zeichnet die benutzerdefinierte Variante eine durchgezogene Linie. Nur wenn die Eigenschaft DashPattern einen Wert aufweist, der von null verschieden ist, wird die in DashPattern definierte Linie gezeichnet. Wir müssen uns deshalb zuerst diese Eigenschaft ansehen:
| Public Property DashPattern As Single()
|
Im ersten Moment irritiert der Typ Single-Array. Aber weil wir es mit einem Array zu tun haben, steht es uns frei, einen beliebigen Linienstil festzulegen. Dazu ein Beispiel:
| Dim p As Pen = New Pen(Color.Black)
|
| p.DashPattern = New Single(){4, 2, 6, 1}
|
Das erste Array-Element legt die Länge der ersten Linie fest, das zweite Array-Element die Länge des sich anschließenden Zwischenraums, das dritte Element die Länge der zweiten Linie, das vierte Element die Länge des darauf folgenden Zwischenraums usw. Es bleibt nur noch zu klären, was unter der Länge verstanden wird. Hierbei spielt die Linienbreite eine entscheidende Rolle, denn es gilt, dass die Länge jedes Striches und jedes Zwischenraums im Strichmuster das Produkt aus dem Wert des Array-Elements und der Breite des Pen-Objekts berechnet.
Das folgende Beispielprogramm zeigt in der Form alle Mitglieder der Enumeration DashStyle an. Für die Ausgabe des Bezeichners wird die Methode DrawString des Graphics-Objekts benutzt, der wir uns ab Abschnitt 22.4 noch zuwenden werden. Innerhalb der Schleife wird eine benutzerdefinierte Strich-Punkt-Linie definiert.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 22\DashStyles
|
| ' ----------------------------------------------------------
|
| Imports System.Drawing.Drawing2D
|
| Public Class Form1
|
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| Dim y As Integer = 0
|
| Dim f As Font = New Font("Courier New", 12)
|
| Dim p As Pen = New Pen(Color.Black, 2)
|
| ' die Member der Enumeration 'DashStyle' durchlaufen
|
| For Each ds As DashStyle In _
|
| [Enum].GetValues(GetType(DashStyle))
|
| ' Name des DashStyles ausgeben
|
| e.Graphics.DrawString(ds.ToString(), Font, ´_
|
| Brushes.Black, 10, y * 20)
|
| ' aktuellen DashStyle dem Pen-Objekt zuweisen
|
| p.DashStyle = ds
|
| ' benutzerdefinierten DashStyle festlegen (Strich-
|
| ' Punkt-Linie)
|
| If ds = DashStyle.Custom Then
|
| p.DashPattern = New Single() {10, 2, 2, 2}
|
| End If
|
| ' Linie zeichnen
|
| e.Graphics.DrawLine(p, 130, 10 + y * 20, 300, 10 + y * 20)
|
| y += 1
|
| Next
|
| End Sub
|
| End Class
|
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.21 Die Konstanten der Enumeration »DashStyle«
Linienverbindungen
Werden Linien, die eine größere Breite haben, mit der Methode DrawLines oder einer der Methoden, die einen Linienzug ergeben, gezeichnet, kann der Punkt, an dem die Linien zusammenstoßen, mit der Eigenschaft LineJoin festgelegt werden.
| Public Property LineJoin As LineJoin
|
Standardmäßig laufen die beiden Linien spitz aufeinander zu. In der Enumeration LineJoin werden darüber hinaus noch Alternativen angeboten, den Verbindungspunkt abzuschrägen oder abzurunden.
Tabelle 22.4 Die Enumeration »LineJoin«
| LineJoin-Member
|
Beschreibung
|
| Miter
|
(Standard) Spitz zulaufende Verbindungslinien
|
| Bevel
|
Mit einer Diagonalen abgeflachte Verbindungslinien
|
| Round
|
Abgerundete Verbindungslinien
|
| MiterClipped
|
Ähnlich Miter, jedoch immer spitzer zulaufend
|
Das folgende Beispielprogramm soll den Effekt von LineJoin demonstrieren und je zwei Linien mit den unterschiedlichen Einstellungen ausgeben.
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 22\Linienverbindung
|
| ' ----------------------------------------------------------
|
| Imports System.Drawing.Drawing2D
|
| Public Class Form1
|
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| Dim p As Pen = New Pen(Brushes.Black, 30)
|
| Dim i As Integer = 0
|
| For Each lj As LineJoin In _
|
| [Enum].GetValues(GetType(LineJoin))
|
| p.LineJoin = lj
|
| Dim pt() As Point = New Point() _
|
| {New Point(i * 110 + 20, 30), _
|
| New Point(i * 110 + 100, 30), _
|
| New Point(i * 110 + 100, 80)}
|
| e.Graphics.DrawLines(p, pt)
|
| i += 1
|
| Next
|
| End Sub
|
| End Class
|
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.22 Linienverbindung, angelegt mit »LineJoin«
Der Stil des Linienzugs
Die Eigenschaft LineJoin zeigt nur dann eine Wirkung, wenn die Linienbreite so groß gewählt wird, dass aus der Linie im Grunde genommen schon eine geometrische Figur wird. Wenn eine Linie mit der Eigenschaft DashStyle nicht mehr durchgezogen, sondern zum Beispiel gestrichelt gezeichnet wird, treten ähnliche Probleme auch innerhalb der Linie auf: Die einzelnen Elemente werden als Blöcke gezeichnet. Vielleicht wünschen Sie sich aber eine Rundung der Elemente oder eine Abschrägung, um die Optik zu verbessern. Dann müssen Sie die Eigenschaft DashCap des Pen-Objekts entsprechend einstellen.
DashCap basiert natürlich ebenfalls auf einer Enumeration, die drei Mitglieder bereitstellt.
Tabelle 22.5 Die Mitglieder der Enumeration »DashCap«
| DashCap-Member
|
Beschreibung
|
| Flat
|
(Standard) Das Ende des Linienelements ist abgeflacht.
|
| Round
|
Das Ende des Linienelements ist abgerundet.
|
| Triangle
|
Das Ende des Linienelements ist abgeschrägt.
|
In der folgenden Abbildung können Sie die Auswirkungen der verschiedenen Einstellungen anhand einer gestrichelten Linie sehen. Die Linie oben ist der Standard und zeigt die abgeflachten Enden, die in der mittleren Linie abgerundet sind. Die unterste Linie hat die Einstellung Triangle.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.23 Die Auswirkung der Eigenschaft »DashCap«
Linienenden
In Abbildung 22.23 fällt auf, dass der Linienanfang davon nicht betroffen ist (und im Übrigen auch nicht das Linienende, das bei der mittleren und unteren Linie in den freien Zwischenraum fällt).
Um Linienanfang und Linienende festzulegen, bietet die Pen-Klasse zwei Eigenschaften an, die Gestaltungsmöglichkeiten über das Abrunden und Abschrägen hinaus offerieren: StartCap und EndCap.
| Public Property StartCap As LineCap
|
| Public Property EndCap As LineCap
|
LineCap gibt die Variationsmöglichkeiten vor und hat die folgenden Mitglieder:
Tabelle 22.6 Die Enumeration »LineCap«
| LineCap-Member
|
Beschreibung
|
| AnchorMask
|
Prüft, ob es sich um einen Anchor handelt.
|
| ArrowAnchor
|
Pfeilförmiges Ankerende
|
| Custom
|
Benutzerdefiniertes Linienende
|
| DiamondAnchor
|
Rautenförmiges Ankerende
|
| Flat
|
Abgeflaches Linienende
|
| NoAnchor
|
Kein Anker
|
| Round
|
Rundes Linienende
|
| RoundAnchor
|
Rundes Ankerende
|
| Square
|
Quadratisches Linienende
|
| SquareAnchor
|
Quadratisches Ankerlinienende
|
| Triangle
|
Dreieckiges Linienende
|
Das folgende Beispielprogramm demonstriert, wie sich die einzelnen Mitglieder von LineCap auf den Linienabschluss auswirken.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.24 Die Linienenden, festgelegt mit »LineCap«
| ' ----------------------------------------------------------
|
| ' Beispiel: ...\Kapitel 22\Linienenden
|
| ' ----------------------------------------------------------
|
| Imports System.Drawing.Drawing2D
|
| Public Class Form1
|
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| Dim p As Pen = New Pen(Color.Blue, 15)
|
| Dim b As Brush = New SolidBrush(Color.Black)
|
| Dim f As Font = New Font("Courier New", 10)
|
| For Each lc As LineCap In _
|
| [Enum].GetValues(GetType(LineCap))
|
| e.Graphics.DrawString(lc.ToString(), f, b, 10, 10)
|
| ' Linienenden festlegen
|
| p.StartCap = lc
|
| p.EndCap = lc
|
| ' Linie zeichnen
|
| e.Graphics.DrawLine(p, 150, 20, 400, 20)
|
| ' Koordinatenursprung verschieben
|
| e.Graphics.TranslateTransform(0, 30)
|
| Next
|
| End Sub
|
| End Class
|
Es bleibt noch ein Punkt zu erwähnen: die Einstellung LineCap.Custom. Dazu wird den Eigenschaften CustomStartCap und CustomEndCap des Pen-Objekts die Referenz auf ein Custom-LineCap-Objekt übergeben, das die Linienenden über die passenden Übergabeparameter an den Konstruktor bestimmt.
Kombinationsmöglichkeiten
Wir sind am Ende der Beschreibung der Klassen Pen und Brush angelangt. Wenn Sie aufmerksam gefolgt sind, haben Sie feststellen können, dass sich beide Typen sehr schön kombinieren lassen, weil zwei der vier Konstruktoren von Pen als Übergabeargument den Typ Brush erwarten. Da eine geometrische Figur wie ein Ansichtsfenster auf die mit einem Brush gezeichnete Grundfläche wirkt, sind den Gestaltungsmöglichkeiten kaum kreative Grenzen gesetzt. Ich möchte Ihnen das an einem einfachen Beispiel zeigen, in dem mit der Klasse PathGradientBrush ein farblicher Übergang definiert wird, der einen Pfeil optisch reizvoll aussehen lässt.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 22.25 Effektvoller Pfeil durch Kombination verschiedener Klassen
| Private Sub Form1_Paint(...) Handles MyBase.Paint
|
| ' das Brush-Objekt festlegen
|
| Dim graphPath As GraphicsPath = New GraphicsPath()
|
| graphPath.AddRectangle(New Rectangle(0, 0, 400, 200))
|
| Dim brush As PathGradientBrush = _
|
| New PathGradientBrush(graphPath)
|
| brush.CenterPoint = New Point(300, 100)
|
| brush.CenterColor = Color.White
|
| brush.SurroundColors = New Color() {Color.Black}
|
| ' das Pen-Objekt definieren
|
| Dim p As Pen = New Pen(brush, 100)
|
| p.EndCap = LineCap.ArrowAnchor
|
| ' einen Pfeil in die Form zeichnen
|
| e.Graphics.DrawLine(p, New Point(40, 100), New Point(400, 100))
|
| End Sub
|
22.3.3 Farbeinstellungen mit »Color«  
Farben haben wir nicht nur in diesem, sondern auch schon in den vergangenen Kapiteln reichlich festgelegt. Wir haben uns dazu immer der Struktur Color bedient und eine der insgesamt 140 vordefinierten Konstanten daraus verwendet.
Wem diese Farbpalette nicht ausreicht, der kann auf eine der vier statischen Methoden From-Argb der Color-Struktur zurückgreifen und eine der über 16 Millionen Farben basierend auf dem Rot-, Grün- oder Blauanteil selbst festlegen:
| Public Shared Function FromArgb(Integer, Integer, Integer) As Color
|
Das erste Argument beschreibt den Rotanteil, das zweite den grünen und das dritte den blauen Anteil. Den jeweiligen Argumenten kann ein Zahlenwert von 0 bis 255 übergeben werden. Dabei gilt, dass
der Farbe Schwarz entspricht und
| Color.FromArgb(255, 255, 255)
|
der Farbe Weiß.
Mit einer überladenen Variante lässt sich ein Farbschema festlegen, das dem ARGB-Modell (Alpha-Rot-Grün-Blau) folgt. Der Alpha-Kanal legt in diesem die Transparenz einer Farbe fest. Dabei bedeutet 255, dass die Farbe vollständig decken soll, und 0, dass die Farbe 100 %ig durchsichtig ist:
| Public Shared Function FromArgb(Integer, Integer, Integer, Integer) As Color
|
Der erste Parameter beschreibt den Alpha-Kanal, die anderen drei die Rot-, Grün- und Blauanteile.
Die drei Anteile Rot, Grün und Blau werden in den Eigenschaften R, G und B von Color festgehalten, der Wert des Alpha-Kanals in A. Alle sind vom Typ Byte.
Systemfarben ermitteln
Es wird etwas schwieriger, mit den Color-Farbeinstellungen zu arbeiten, wenn Sie auf die Systemfarben von Windows zurückgreifen wollen, da Sie nicht wissen können, wie der Anwender das Schema individuell eingestellt hat. In diesem Fall können Sie die Farben nicht statisch codieren, sondern müssen diese zur Laufzeit dynamisch ermitteln. Dazu dient die Klasse SystemColors, die sich dem Namespace System.Drawing eingliedert.
In dieser Klasse ist eine stattliche Anzahl statischer Eigenschaften vordefiniert, die als Rückgabewert den Typ Color liefern. Mit der Eigenschaft Window können Sie zum Beispiel die aktuelle Hintergrundfarbe des Clientbereichs eines Fensters feststellen. Das ist dann nicht die, die über die Eigenschaft BackColor zugewiesen worden ist, sondern diejenige, deren Schema aufgrund der Einstellung im Eigenschaftsfenster des Desktops aktuell der Standard ist.
|